/**************************************************************************//**
 * @file    NC_Helper.c
 * @author  İsmail SEZEN (email: sezenismail@gmail.com)
 * @date    02-08-2010
 *
 * @note This is part of siklon project. (Siklon.c)
 *
 * <b>Description</b>
 *
 * This file includes definitions and functions to help manipulation
 * of NC files contains geoptantial height data.\n
 *
 * Copyright (C) 2009 İsmail SEZEN
 *
 * <b>License</b>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *****************************************************************************/


/** Include Directives */ //{
#include <stdlib.h>
#include <stdio.h>
#include <netcdf.h>
#include <udunits.h>
#include <logger.h>
#include <array_helper.h>

#include "Helpers.h"
#include "siklon_internal.h"
#include "AttributeHelper.h"
#include "UtunitsHelper.h"
//}

/** Define Directives */ //{

/* Attributes *///{
#define ATT_ACTUAL_RANGE "actual_range"
#define ATT_UNITS "units"
#define ATT_DELTA_T "delta_t"
#define ATT_LONG_NAME "long_name"
#define ATT_VALID_RANGE "valid_range"
#define ATT_ADD_OFFSET "add_offset"
#define ATT_SCALE_FACTOR "scale_factor"
#define ATT_MISSING_VALUE "missing_value"
#define ATT_GRIB_ID "GRIB_id"
#define ATT_GRIB_NAME "GRIB_name"
#define ATT_VAR_DESC "var_desc"
#define ATT_POSITIVE "positive"
//}

/* Names of things. *///{
#define NAME_LAT "lat" //Latitude Name
#define NAME_LON "lon" //Longtitude Name
#define NAME_TIME "time"//Time name
#define NAME_LEVEL "level"//Level Name
#define NAME_HGT "hgt"//Name of the variable
//}

//}

/** Macros */ //{
#define ERR(e) {printf("Error: %s\n", nc_strerror(e)); return e;}/* Handle errors by printing an error message and exiting with a non-zero status. */
#define ERR2(e) {log_fat("Error: %s\n", nc_strerror(e));}
#define ERR3(e, info) {log_fat("Error: %s - %s\n", nc_strerror(e), info)}
//}

void NC_open_file(char* fileName) {
	int e =NC_NOERR;
	/* Open the file. */
	if ( (e=nc_open(fileName, NC_NOWRITE, &info.id.nc)) ) ERR3(e,fileName);
}

size_t NC_get_dim_length_by_id(int ncid, int id) {
	int e =NC_NOERR;
	size_t length = 0;
	if ( (e=nc_inq_dimlen(ncid, id, &length)) ) ERR2(e);
	return length;
}

size_t NC_get_dim_length_by_name(int ncid, char* name) {
	int e =NC_NOERR;
	int id=0;
	if ( (e=nc_inq_dimid(ncid, name, &id)) ) ERR2(e);
	return NC_get_dim_length_by_id(ncid, id);
}

void NC_get_dim_props(int ncid, char* name,struct dimension* d) {
	int e =NC_NOERR;
	int id=0;
	size_t len = 0;
	if ( (e=nc_inq_dimid(ncid, name, &id)) ) ERR2(e);
	len = NC_get_dim_length_by_id(ncid, id);
	d->id = id;
	d->len = len;
}

void NC_get_variable_ids() {
	int e =NC_NOERR;
	//int ncid, hgt_varid, lon_varid, lat_varid, time_varid;//level_varid;
	if(info.id.nc > 0) {
		/* Get the varids of the hgt, time, latitude and longitude coordinate variables of the netCDF file. */
		if ( (e = nc_inq_varid(info.id.nc,  NAME_LEVEL, &info.id.level)) ) ERR2(e);/*Get level varid*/
		if ( (e = nc_inq_varid(info.id.nc, NAME_HGT, &info.id.hgt)) ) ERR2(e);/*Get hgt var id*/
		if ( (e = nc_inq_varid(info.id.nc, NAME_TIME, &info.id.time)) ) ERR2(e);/*Get time varid*/
		if ( (e = nc_inq_varid(info.id.nc, NAME_LAT, &info.id.lat)) ) ERR2(e);/*Get lattitude varid*/
		if ( (e = nc_inq_varid(info.id.nc, NAME_LON, &info.id.lon)) ) ERR2(e);/*Get longtitude varid*/
	}
}

void NC_read_dimension_lengths() {
	//Dimensions
	nc_inq_ndims(info.id.nc, &info.dims.count);
	NC_get_dim_props(info.id.nc, NAME_LEVEL, &info.dims.level);
	NC_get_dim_props(info.id.nc, NAME_LAT, &info.dims.lat);
	NC_get_dim_props(info.id.nc, NAME_LON, &info.dims.lon);
	NC_get_dim_props(info.id.nc, NAME_TIME, &info.dims.time);
}

void NC_read_attributes() {
	double* dp;
	float* fp;
	/* level attributes */
	fp= get_float_att_p(info.id.nc, info.id.level, ATT_ACTUAL_RANGE);
	info.attr.level.actual_range.min = fp[0];
	info.attr.time.actual_range.max = fp[1];
	free(fp);
	info.attr.level.units  = get_string_att_p(info.id.nc, info.id.level,ATT_UNITS);
	info.attr.level.long_name = get_string_att_p(info.id.nc, info.id.level,ATT_LONG_NAME);
	info.attr.level.positive = get_string_att_p(info.id.nc, info.id.level, ATT_POSITIVE);
	info.attr.level.GRIB_id = get_short_att(info.id.nc, info.id.level, ATT_GRIB_ID);
	info.attr.level.GRIB_name = get_string_att_p(info.id.nc, info.id.level, ATT_GRIB_NAME);
	/*lat attributes */
	fp= get_float_att_p(info.id.nc, info.id.lat, ATT_ACTUAL_RANGE);
	info.attr.lat.actual_range.min = fp[1];
	info.attr.lat.actual_range.max = fp[0];
	free(fp);
	info.attr.lat.units  = get_string_att_p(info.id.nc, info.id.lat, ATT_UNITS);
	info.attr.lat.long_name = get_string_att_p(info.id.nc, info.id.lat, ATT_LONG_NAME);
	/*lon attributes */
	fp= get_float_att_p(info.id.nc, info.id.lon, ATT_ACTUAL_RANGE);
	info.attr.lon.actual_range.min = fp[0];
	info.attr.lon.actual_range.max = fp[1];
	free(fp);
	info.attr.lon.units  = get_string_att_p(info.id.nc, info.id.lon, ATT_UNITS);
	info.attr.lon.long_name = get_string_att_p(info.id.nc, info.id.lon, ATT_LONG_NAME);
	/* Time Attributes */
	dp= get_double_att_p(info.id.nc, info.id.time, ATT_ACTUAL_RANGE);
	info.attr.time.actual_range.min = dp[0];
	info.attr.time.actual_range.max = dp[1];
	free(dp);
	info.attr.time.units  = get_string_att_p(info.id.nc, info.id.time,ATT_UNITS);
	info.attr.time.delta_t= get_string_att_p(info.id.nc, info.id.time, ATT_DELTA_T);/* Get the time units*/
	info.attr.time.long_name = get_string_att_p(info.id.nc, info.id.time,ATT_LONG_NAME);

	/* Variable Attributes (for instance, hgt...) */
	info.attr.hgt.long_name = get_string_att_p(info.id.nc, info.id.hgt,ATT_LONG_NAME);
	fp = get_float_att_p(info.id.nc, info.id.hgt, ATT_ACTUAL_RANGE);
	info.attr.hgt.actual_range.min = fp[0];
	info.attr.hgt.actual_range.max = fp[1];
	free(fp);
	fp= get_float_att_p(info.id.nc, info.id.hgt, ATT_VALID_RANGE);
	info.attr.hgt.valid_range.min = fp[0];
	info.attr.hgt.valid_range.max = fp[1];
	free(fp);
	info.attr.hgt.units = get_string_att_p(info.id.nc, info.id.hgt,ATT_UNITS);
	info.attr.hgt.addOffset = get_float_att(info.id.nc, info.id.hgt,ATT_ADD_OFFSET);
	info.attr.hgt.scaleFactor = get_float_att(info.id.nc, info.id.hgt,ATT_SCALE_FACTOR);
	info.attr.hgt.missing_value = get_short_att(info.id.nc, info.id.hgt,ATT_MISSING_VALUE);
	info.attr.hgt.GRIB_id = get_short_att(info.id.nc, info.id.hgt,ATT_GRIB_ID);
	info.attr.hgt.GRIB_name = get_string_att_p(info.id.nc, info.id.hgt,ATT_GRIB_NAME);
	info.attr.hgt.var_desc = get_string_att_p(info.id.nc, info.id.hgt,ATT_VAR_DESC);
	/*------------------------------------------------------------------------------------------------------------------------*/
}

void NC_read_coord_var_data() {
	int e =NC_NOERR;
	//allocate enough memory for dims
	dom.dims.times = arr_malloc(double, info.dims.time.len);
	dom.dims.levs = arr_malloc(float, info.dims.level.len);
	dom.dims.lats = arr_malloc(float, info.dims.lat.len);
	dom.dims.lons = arr_malloc(float, info.dims.lon.len);

	/* Read the coordinate variable data. */
	if ( (e = nc_get_var_float(info.id.nc, info.id.level, &dom.dims.levs[0])) ) ERR2(e);
	if ( (e = nc_get_var_double(info.id.nc, info.id.time, &dom.dims.times[0])) ) ERR2(e);
	if ( (e = nc_get_var_float(info.id.nc, info.id.lat, &dom.dims.lats[0])) ) ERR2(e);
	if ( (e = nc_get_var_float(info.id.nc, info.id.lon, &dom.dims.lons[0])) ) ERR2(e);
}

static void print_var_data(float* arr, int len) {
	register int i;
	for(i=0;i<len;i++) printf("%04.1f, ",arr[i]);
}

void NC_print_coord_var_data() {
	printf("\nlevels:\n");
	print_var_data(dom.dims.levs, info.dims.level.len);
	printf("\n\nlats:\n");
	print_var_data(dom.dims.lats, info.dims.lat.len);
	printf("\n\nlons:\n");
	print_var_data(dom.dims.lons, info.dims.lon.len);
	utUnit  unit;
	if (udunits_start_scan(info.attr.time.units,&unit) == 0) {}
	printf("\n\ntimes:\n");
	register int i = 0;
	double old_t = 0.0;
	for(i=0;i<info.dims.time.len;i++){
		double t = dom.dims.times[i];
		printf("%d)\t%.0f\t%.0f\t",i,t,(t-old_t));
		print_time(&t,&unit);
		printf("\n");
		old_t = t;
	}
}

void NC_get_data(short* data, const size_t *startp, const size_t *countp) {
	int retval;/* Error handling. */
	if ((retval = nc_get_vara_short(info.id.nc, info.id.hgt, startp, countp, data))) ERR2(retval);/*Read Data*/
}


